﻿#pragma once

#include <memory>
#include <vector>
#include <glm/glm.hpp>
#include <filesystem>
#include <functional>
#include <map>
#include <assimp/scene.h>

#include "MaterialSrc/MeshMaterial.h"


class FLightBase;
class FMaterial;
class FTexture2D;

struct FVertexInfo
{
	glm::vec3 Position;
	glm::vec3 Normal;
	glm::vec2 TexCoords;
};


struct FDrawArg
{
	float DateTime = 0.1f;
	std::vector<std::shared_ptr<FLightBase>> LightArray;
	glm::mat4 ViewMat;
	glm::mat4 ProjectionMat;
	glm::vec3 ViewPos;
};


class FMesh
{
public:
	virtual ~FMesh() = default;

	bool Init(std::vector<FVertexInfo>&& VertexArr, std::vector<int32_t>&& IndexArr, std::shared_ptr<FMaterial>&& InMaterial);

	bool Init(std::vector<FVertexInfo>&& VertexArr, std::shared_ptr<FMaterial>&& InMaterial);
	bool Init(const std::vector<FVertexInfo>& VertexArr, std::shared_ptr<FMaterial> InMaterial);


	bool InitVertex(const std::vector<FVertexInfo>& VertexArr);
	bool InitIndex(const std::vector<int32_t>& IndexArr);

	void SetMaterial(const std::shared_ptr<FMaterial>& InMaterial) { Material = InMaterial;}

	std::shared_ptr<FMaterial> GetMaterial()const { return  Material; }


	virtual void OnInit() {};
	virtual void Draw(const FDrawArg& DrawArg) const;

	void SetTransform(const glm::mat4& InTransform) { Transform = InTransform; }

	std::function<void(std::shared_ptr<FShaderProgram> Shader)> OnSetShaderData;
	std::function<void(std::map<std::string, bool>& EnabledSetMap)> OnCheckEnabledSetShaderData;

protected:

	bool InitVertexInfo();
	bool InitIndexInfo();

protected:

	std::shared_ptr<FMaterial> Material;

	std::vector<FVertexInfo> VertexArray;

	std::vector<int32_t> IndexArray;

	uint32_t VAO = 0;

	bool UsedIndexDraw = false;

	glm::mat4 Transform = glm::mat4(1.0f);

private:

	uint32_t VBO = 0;

	uint32_t EBO = 0;
};


class FMeshModel
{
public:
	enum ETextureType
	{
		DiffuseShininessr,
		Diffuse,
	};


	virtual ~FMeshModel() = default;

	bool Load(const std::filesystem::path& ModelAssetDirPath, const std::string& AssetFileName);

	void Draw(const FDrawArg& DrawArg) const;

	void SetTransform(const glm::mat4& InTransform) const
	{
		for (const auto& Mesh : MeshArray)
		{
			Mesh->SetTransform(InTransform);
		}
	}

	std::function<std::shared_ptr<FMaterial>(ETextureType Type)> CustomCreateMaterialCall;

private:

	[[nodiscard]] virtual std::shared_ptr<FMesh> CreateMesh() const;

	[[nodiscard]] virtual  std::shared_ptr<FMaterial> CreateMaterial(ETextureType Type) const;

	template<typename T>
	[[nodiscard]] std::shared_ptr<T> CreateMaterialInstance(ETextureType Type)const
	{
		return std::static_pointer_cast<T>(CreateMaterial(Type));
	}

private:

	void ProcessNode(const aiNode* Node, const aiScene* Scene);
	void LoadMaterialTextures(const aiMaterial* Mat, aiTextureType Type, std::string TypeName,
		std::vector<FTextureInfo>& OutTextureInfoArr) const;

	std::shared_ptr<FMesh> ProcessMesh(const aiMesh* Mesh, const aiScene* Scene) const;

protected:

	std::filesystem::path AssetDirPath;
	std::vector<std::shared_ptr<FMesh>> MeshArray;

};